#include <math.h>
#include "../common_typedef.h"
#include "../cominc/drv_DigitalTube.h"

static uint8_t g_codeTab[][8] = {
        {1,1,1,1,1,1,0,3}, // '0'
        {0,1,1,0,0,0,0,2}, // '1'
        {1,1,0,1,1,0,1,2}, // '2'
        {1,1,1,1,0,0,1,2}, // '3'
        {0,1,1,0,0,1,1,2}, // '4'
        {1,0,1,1,0,1,1,2}, // '5'
        {1,0,1,1,1,1,1,3}, // '6'
        {1,1,1,0,0,0,0,1}, // '7'
        {1,1,1,1,1,1,1,3}, // '8'
        {1,1,1,1,0,1,1,2}, // '9'
        {1,1,1,1,1,1,0,3}, // 'O'
        {1,0,0,0,1,1,1,2}, // 'F'
};

/* 内部依赖方法 */
static uint32_t isDrvDigitalTubeValid(const drvDigitalTube* pTubeIO)
{
    if (pTubeIO == NULL) {
        return DRV_INVALID;
    }
    for (drvDigitalTubeIO* itr = (drvDigitalTubeIO*)pTubeIO; itr != pTubeIO->digEn; itr++) {
        if (itr == NULL) {
            return DRV_INVALID;
        }
    }
    for (size_t idx = 0; idx < DIG_CH_SIZE; idx++) {
        if (pTubeIO->digEn[idx] == NULL) {
            return DRV_INVALID;
        }
    }
    return DRV_VALID;
}

static void drvDigitalTubeSelect(const drvDigitalTube* pTubeIO, int digNum)
{
    if (!isDrvDigitalTubeValid(pTubeIO) || digNum >= DIG_CH_SIZE) {
        return;
    }
    // all off
    for (size_t idx = 0; idx < DIG_CH_SIZE; idx++) {
        pTubeIO->digEn[idx](DIG_ON);
    }
    pTubeIO->digDP(DIG_OFF);
    pTubeIO->digA(DIG_OFF);
    pTubeIO->digB(DIG_OFF);
    pTubeIO->digC(DIG_OFF);
    pTubeIO->digD(DIG_OFF);
    pTubeIO->digE(DIG_OFF);
    pTubeIO->digF(DIG_OFF);
    pTubeIO->digG(DIG_OFF);
    // turn on
    pTubeIO->digEn[digNum](DIG_OFF);
}

static void drvDigitalTubeShowSingleNum(drvDigitalTube* pTubeIO, int num, uint32_t isDP)
{
    if (!isDrvDigitalTubeValid(pTubeIO)) {
        return;
    }
    num = (num > sizeof(g_codeTab) ? sizeof(g_codeTab) : num);
    num = (num <= 0 ? 0 : num);
    pTubeIO->digDP(isDP);
    pTubeIO->digA(g_codeTab[num][0]);
    pTubeIO->digB(g_codeTab[num][1]);
    pTubeIO->digC(g_codeTab[num][2]);
    pTubeIO->digD(g_codeTab[num][3]);
    pTubeIO->digE(g_codeTab[num][4]);
    pTubeIO->digF(g_codeTab[num][5]);
    pTubeIO->digG(g_codeTab[num][6]);
}
/* 对外接口 */
void drvDigitalTubeClear(drvDigitalTube* pTubeIO)
{
    if (!isDrvDigitalTubeValid(pTubeIO)) {
        return;
    }
    /* 处理共阴极数码管 */
    for (size_t idx = 0; idx < DIG_CH_SIZE; idx++) {
        pTubeIO->digEn[idx](DIG_ON);
        pTubeIO->timePeriod[idx] = 0;
    }
    pTubeIO->timeCnt = 0;
}

void floatExtract(drvDigitalTube* pTubeIO, float num)
{
    num = (num > 999.9f ? 999.9f : num);
    num = (num < 0.00f ? 0.00f : num);
    // 先按照0.01为单位定点化
    long int validNum = lround(num * 100.0);
    // 改为科学计数法
    if (validNum <= 999) { // 显示值 [0.00, 9.99]
        pTubeIO->digPos = 0;
    } else if (validNum <= 9999) { // 显示值 [10.0, 99.9]
        validNum = lround(num * 10.0);
        pTubeIO->digPos = 1;
    } else { // 显示值 [100, 999]
        validNum = lround(num * 1.0);
        pTubeIO->digPos = 2; // 不再显示小数点
    }
    pTubeIO->num[0] = (validNum / 100) % 10;
    pTubeIO->num[1] = (validNum / 10) % 10;
    pTubeIO->num[2] = validNum % 10;
    pTubeIO->timePeriod[0] = g_codeTab[pTubeIO->num[0]][7];
    pTubeIO->timePeriod[1] = pTubeIO->timePeriod[0] + g_codeTab[pTubeIO->num[1]][7];
    pTubeIO->timePeriod[2] = pTubeIO->timePeriod[1] + g_codeTab[pTubeIO->num[2]][7];
    pTubeIO->timeCnt = 0;
}

// 按照0.01进行定点化，输出到3位数码管
// 数码管值域 [0.01~999]
void drvDigitalTubeShowFloat(drvDigitalTube* pTubeIO, float floatNumber)
{
    if (!isDrvDigitalTubeValid(pTubeIO)) {
        return;
    }
    if (isDrvDigitalTubeDisplayDone(pTubeIO)) {
        // 若上个周期结束，则显示新的周期，float定点化为整形+浮点位置
        floatExtract(pTubeIO, floatNumber);
    }
    // 完成显示
    int digPos = 0;
    if (pTubeIO->timeCnt < pTubeIO->timePeriod[0]) {
        digPos = 0;
    } else if (pTubeIO->timeCnt < pTubeIO->timePeriod[1]) {
        digPos = 1;
    } else if (pTubeIO->timeCnt < pTubeIO->timePeriod[2]) {
        digPos = 2;
    }
    if (pTubeIO->timeCnt < pTubeIO->timePeriod[2]) {
        pTubeIO->timeCnt++;
    }
    drvDigitalTubeSelect(pTubeIO, digPos);
    drvDigitalTubeShowSingleNum(pTubeIO, pTubeIO->num[digPos], digPos == pTubeIO->digPos);
}

int isDrvDigitalTubeDisplayDone(const drvDigitalTube* pTubeIO)
{
    return pTubeIO->timeCnt == pTubeIO->timePeriod[DIG_CH_SIZE - 1];
}


// 按照0.01进行定点化，输出到3位数码管
// 数码管值域 [0.01~999]
void drvDigitalTubeShowOFF(drvDigitalTube* pTubeIO)
{
    if (!isDrvDigitalTubeValid(pTubeIO)) {
        return;
    }
    if (isDrvDigitalTubeDisplayDone(pTubeIO)) {
        // 若上个周期结束，则显示新的周期，
        pTubeIO->num[0] = 10;
        pTubeIO->num[1] = 11;
        pTubeIO->num[2] = 11;
        pTubeIO->timePeriod[0] = g_codeTab[pTubeIO->num[0]][7];
        pTubeIO->timePeriod[1] = pTubeIO->timePeriod[0] + g_codeTab[pTubeIO->num[1]][7];
        pTubeIO->timePeriod[2] = pTubeIO->timePeriod[1] + g_codeTab[pTubeIO->num[2]][7];
        pTubeIO->digPos = 2; // 不再显示小数点
        pTubeIO->timeCnt = 0;
    }
    // 完成显示
    int digPos = 0;
    if (pTubeIO->timeCnt < pTubeIO->timePeriod[0]) {
        digPos = 0;
    } else if (pTubeIO->timeCnt < pTubeIO->timePeriod[1]) {
        digPos = 1;
    } else if (pTubeIO->timeCnt < pTubeIO->timePeriod[2]) {
        digPos = 2;
    }
    if (pTubeIO->timeCnt < pTubeIO->timePeriod[2]) {
        pTubeIO->timeCnt++;
    }
    drvDigitalTubeSelect(pTubeIO, digPos);
    drvDigitalTubeShowSingleNum(pTubeIO, pTubeIO->num[digPos], digPos == pTubeIO->digPos);
}